package com.violet.service.app.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.violet.domain.dto.app.AUserDto;
import com.violet.domain.emuns.OrderStatus;
import com.violet.domain.po.BuyOrder;
import com.violet.domain.po.BuyProduct;
import com.violet.domain.po.User;
import com.violet.domain.vo.app.AUserOrderVo;
import com.violet.domain.vo.app.AUserVo;
import com.violet.mapper.BuyOrderMapper;
import com.violet.mapper.BuyProductMapper;
import com.violet.mapper.UserMapper;
import com.violet.properties.FileUploadProperties;
import com.violet.service.app.AUserService;
import com.violet.utils.*;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Stream;

@Service
@RequiredArgsConstructor
public class AUserServiceImpl extends ServiceImpl<UserMapper, User>
        implements AUserService {

    private final TransitionUtils<?, User, AUserVo> transition;
    private final FileUtils fileUtils;
    private final FileUploadProperties properties;

    private final BuyOrderMapper buyOrderMapper;
    private final BuyProductMapper buyProductMapper;

    private final TransitionUtils<?, BuyOrder, AUserOrderVo.AUserOrderItem> transition2;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AUserVo login(AUserDto dto) {
        // 数据库中是否有该用户
        User user = baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, dto.getPhone()));
        // 如果不存在就注册
        if (user == null) {
            try {
                user = POUtils.getUser(dto.getPhone());
                baseMapper.insert(user);
            } catch (DuplicateKeyException e) {
                // 并发插入导致重复
                user = baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, dto.getPhone()));
                if (user == null) throw new RuntimeException("服务器异常，请稍后再试");
            }
        }
        // 校验验证码
        CodeUtils.checkCode(dto.getPhone(), dto.getCode());
        // 生成 token
        String token = JwtTokenUtil.generateToken(user.getId());
        // 设置最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        baseMapper.updateById(user);
        // 转换成 VO, 返回
        return transition.transition(user, AUserVo.class).setToken(token);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean rename(String name) {
        int update = baseMapper.update(null,
            new LambdaUpdateWrapper<User>()
                .set(User::getUsername, name)
                .eq(User::getId, UserContent.getUserId())
        );
        return update == 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String avatar(MultipartFile file) {
        String id = UserContent.getUserId();
        User user = baseMapper.getByIdWithPessimisticLock(id);
        if (user == null) throw new RuntimeException("用户不存在");
        String image = null;
        try {
            image = fileUtils.uploadImage(file);
            int updateRows = baseMapper.update(null,
                new LambdaUpdateWrapper<User>()
                    .set(User::getImage, image)
                    .eq(User::getId, id)
            );
            if (updateRows > 0) {
                fileUtils.deleteImage(user.getImage());
            }
        }
        catch (Exception e) {
            // 删除上传失败的文件
            if (image != null) fileUtils.deleteImage(image);
            throw new RuntimeException("上传失败");
        }
        return properties.getImage().url() + image;
    }

    @Override
    public AUserOrderVo recent() {
        // 1.查询待支付订单的数量
        Long waitPaidCount = buyOrderMapper.selectCount(new LambdaQueryWrapper<BuyOrder>()
            .eq(BuyOrder::getUserId, UserContent.getUserId())
            .eq(BuyOrder::getStatus, OrderStatus.WAIT_PAID)
        );
        // 2.查询待发货订单的数量
        Long waitDeliveryCount = buyOrderMapper.selectCount(new LambdaQueryWrapper<BuyOrder>()
            .eq(BuyOrder::getUserId, UserContent.getUserId())
            .eq(BuyOrder::getStatus, OrderStatus.WAIT_DELIVERY)
        );
        // 3.查询待收货订单的数量
        Long waitReceivingCount = buyOrderMapper.selectCount(new LambdaQueryWrapper<BuyOrder>()
            .eq(BuyOrder::getUserId, UserContent.getUserId())
            .eq(BuyOrder::getStatus, OrderStatus.WAIT_RECEIVING)
        );
        // 4.查询已完成订单的数量
        Long completedCount = buyOrderMapper.selectCount(new LambdaQueryWrapper<BuyOrder>()
            .eq(BuyOrder::getUserId, UserContent.getUserId())
            .eq(BuyOrder::getStatus, OrderStatus.COMPLETED)
        );
        // 5. 拿到用户最近的三个订单
        List<BuyOrder> orders = buyOrderMapper.selectList(new LambdaQueryWrapper<BuyOrder>()
            .eq(BuyOrder::getUserId, UserContent.getUserId())
            .orderByDesc(BuyOrder::getCreateTime)
            .last("limit 3")
        );

        Stream<AUserOrderVo.AUserOrderItem> stream = orders.stream().map(buyOrder -> {
            // 获取订单对应的商品的图片地址
            List<String> images = buyProductMapper.selectList(new LambdaQueryWrapper<BuyProduct>().eq(BuyProduct::getOrderId, buyOrder.getId()))
                    .stream()
                    .map(BuyProduct::getImage)
                    .map(item -> properties.getImage().url() + item)
                    .toList();

            // 封装
            return transition2.transition(buyOrder, AUserOrderVo.AUserOrderItem.class)
                    .setStatus(buyOrder.getStatus().message)
                    .setProducts(images);
        });

        return new AUserOrderVo()
            .setItems(stream.toList())
            .setWaitPaidCount(waitPaidCount)
            .setWaitDeliveryCount(waitDeliveryCount)
            .setWaitReceivingCount(waitReceivingCount)
            .setCompletedCount(completedCount);
    }

}
